#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <cassert>
#include<cstdio>
#include <ctime>
#include <unistd.h>
#include<sys/types.h>
#include<sys/wait.h>


#define PROCESS_NUM 5

class SubEP // 把子进程和管道描述起来
{
public:
    SubEP(int writefd, pid_t pid)
        : _writefd(writefd), _pid(pid)
    {
        char tmp[1024];
        sprintf(tmp, "process_number[%d]; pid: %d; writefd：%d",_cnt++, pid, writefd);
        _name = std::string(tmp);
    }

public:
    static int _cnt;
    std::string _name;
    int _writefd;
    pid_t _pid;
};
int SubEP::_cnt = 1;


///////////////////////////////////////////////////////////模拟任务/////////////////////
typedef void(* func_t)();//对函数指针类型重命名为func

void func1()
{    
    sleep(1);
    std::cout<<"任务1：打印任务"<<std::endl;
}

void func2()
{
    sleep(1);
    std::cout<<"任务2：输出任务"<<std::endl;
}

void func3()
{
    sleep(1);
    std::cout<<"任务3：写入任务"<<std::endl;
}

void func4()
{
    sleep(1);
    std::cout<<"任务4：下载任务"<<std::endl;
}

void func5()
{
    sleep(1);
    std::cout<<"任务5：其他任务"<<std::endl;
}
void LoadTask( std::vector<func_t>* funcmap)//加载任务
{
    funcmap->push_back(func1);
    funcmap->push_back(func2);
    funcmap->push_back(func3);
    funcmap->push_back(func4);
    funcmap->push_back(func5);
}

void CreateSubProcess(std::vector<SubEP> *subs, std::vector<func_t>& funcmap)
{
    int i =0;
    std::vector<int> v;
    for (i = 0; i < PROCESS_NUM; i++)
    {
        // 创建管道
        int fds[2];
        int ret = pipe(fds); // 成功返回0；
        assert(ret == 0);
        (void)ret; // 防止对未使用的ret报警告。
        v.push_back(fds[1]);

        // 创建子进程，
        int id = fork();
        if (id == 0)
        {
            // 子进程执行代码
            //close(fds[1]); // 子进程读取指令//关闭写入端
            //我们这样写会出现一个问题，在开始创建第二个进程的时候，此时父进程的文件描述符表，
            //不只有第二个管道的读取写入的fd，还有第一个进程的写入端的文件描述符（读取端口被关闭）
            //所以此时第二个进程会继承到第一个管道的写入文件描述符，这不符合我们的意思。
            //所以要用到下面的方式。
            //所以把所有文件写入端口存入vector中。
            for(int i = 0; i< v.size(); i++)
            {
                close(v[i]);
            }
            
            while(true)
            {
                int code =0 ;
                int n = read(fds[0], &code, 4);
                if(n == 4)//读到的必须是四个字节
                {
                    funcmap[code]();
                }
                else if (n == 0 )//父进程关闭写端文件描述符，子进程读到0 ，退出。
                { break; }
                else
                {std::cout<<"读取错误！！"<<std::endl;}
            }
            exit(0);
        }
        // 父进程执行代码
        close(fds[0]); // 父进程写入指令，//关闭读取端口
        SubEP sub(fds[1], id);
        subs->push_back(sub);
    }
}



//获取一个随机树
int RandomNumber(int n)
{
    return rand() % n;
}

int main()
{
    srand((unsigned long)time(NULL)^12345454);//产生随机数种子。 
    //后面的数字是为了增加随机性

    std::vector<func_t> funcmap;//把任务组织起来
    LoadTask(&funcmap);
    //任务全部加载到funcmap中了。

    std::vector<SubEP> subs; // 把子进程和管道组织起来
    // 建立多个子进程和对应的管道,并且传入对应的任务列表
    CreateSubProcess(&subs, funcmap);
    // 子进程和对应的管道创建完成，并且将信息保存到subs里面去了

    // 父进程开始控制子进程
    int processcnt =subs.size();
    int taskcnt =funcmap.size();
    int count = 5;//循环次数
    //如果大于等于0 ，如若为-1，无限执行。
    while(count)
    {
        //选择一个子进程  //std::vector<SubEP> ->> index
        int processindex = RandomNumber(processcnt);

        //选择一个任务   // std::vector<func_t> ->> index
        int taskindex = RandomNumber(taskcnt);

        //把任务通过管道发送给子进程
        //给选的任务发送四个字节的任务码
        std::cout<<"send task num:"<<taskindex+1<<' '<<"task to ->"<<subs[processindex]._name<<std::endl;
        int ret = write(subs[processindex]._writefd, &taskindex, 4);
        assert(ret == 4);
        (void)ret;
        sleep(2);//每隔两秒发送一个任务码
        if(count != -1)
        {
            count--;
        }
    }
    
    //回收子进程
    //关闭写端文件描述符
    for(int i  =0 ; i< subs.size(); i++){
        close(subs[i]._writefd); 
        waitpid(subs[i]._pid, nullptr,0); 
        sleep(1);
        std::cout<<subs[i]._name<<"--->"<<"等待完成"<<std::endl;
    }
    // for(int i  =subs.size()-1; i>=0; i--){
    //     close(subs[i]._writefd); 
    //     waitpid(subs[i]._pid, nullptr,0); 
    //     sleep(1);
    //     std::cout<<subs[i]._name<<"--->"<<"等待完成"<<std::endl;
    // }

    // for(int i  =0 ; i <subs.size() ; i++)
    // {    
    //     waitpid(subs[i]._pid, nullptr,0); 
    //     sleep(1);
    //     std::cout<<subs[i]._name<<"--->"<<"等待完成"<<std::endl;
    // }

    // for(int i  =subs.size()-1; i >=0 ; i--)
    // {    
    //     waitpid(subs[i]._pid, nullptr,0); 
    //     sleep(1);
    //     std::cout<<subs[i]._name<<"--->"<<"等待完成"<<std::endl;
    // }
    return 0;
}